﻿using System;
using Castle.Services.Transaction;
using Hippo.Core.Exceptions;
using Hippo.Core.Data;
using Hippo.Core.Utility;
using Hippo.Core.Model;
using Hippo.Core.Specifications;

namespace Hippo.Core
{
    [Transactional]
    public class UserService : IUserService, IAuthenticationService
    {
        private IRepository repository;

        public UserService(IRepository repository)
        {
            this.repository = repository;
        }

        #region IUserService Members

        [Transaction(TransactionMode.Requires)]
        public void CreateUser(User user)
        {
            ValidateUser(user);

            user.Id = 0;
            user.Status = UserStatus.Inactive;
            user.CreatedTime = DateTime.Now;
            user.UpdatedTime = user.CreatedTime;

            repository.Create(user);
        }        

        public User GetUserById(int userId)
        {
            User user = repository.GetById<User>(userId);
            if (user == null)
            {
                throw new UserNotFoundException("User #" + userId + " not found.");
            }
            return user;
        }

        public User GetUserByEmail(string email)
        {
            User user = repository.FindFirst(new UserHasEmailSpecification(email));
            if (user == null)
            {
                throw new UserNotFoundException("User has email '" + email + "' not found.");
            }
            return user;
        }

        public User GetUserByUsername(string username)
        {
            User user = repository.FindFirst(new UserHasUsernameSpecification(username));
            if (user == null)
            {
                throw new UserNotFoundException("User '" + username + "' not found.");
            }
            return user;
        }

        [Transaction(TransactionMode.Requires)]
        public void ChangePassword(string username, string oldPassword, string newPassword)
        {
            User user = GetUserByUsername(username);

            if (String.Compare(user.Password, oldPassword) != 0)
            {
                throw new InvalidPasswordException("Old password is incorrect.");
            }
            user.Password = newPassword;

            repository.Update(user);
        }

        #endregion

        #region IAuthenticationService Members

        public User ValidateUser(string username, string password)
        {
            try
            {
                User user = GetUserByUsername(username);
                if (String.Compare(user.Password, password) != 0)
                {
                    throw new AuthenticationException("Username of password is not valid.");
                }
                return user;
            }
            catch (UserNotFoundException)
            {
                throw new AuthenticationException("Username of password is not valid.");
            }                        
        }

        #endregion

        private void ValidateUser(User user)
        {
            Check.Require(!String.IsNullOrEmpty(user.Username), "Username is null or empty.");
            Check.Require(!String.IsNullOrEmpty(user.Email), "Email is null or empty.");

            User existUser = repository.FindFirst(new UserHasUsernameSpecification(user.Username));
            if (existUser != null && user.Id != existUser.Id)
            {
                throw new DuplicateUsernameException();
            }

            existUser = repository.FindFirst(new UserHasEmailSpecification(user.Email));
            if (existUser != null && user.Id != existUser.Id)
            {
                throw new DuplicateEmailException();
            }
        }        
    }
}
